import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class HelpFunctions {
	/**
	 * 
	 * @param f1 - ZIP file full name
	 * @param add - path of 
	 */
	public static boolean addToZip(String f1,String add)
	{
		Log.debug("Adding "+add+" to file: "+f1);
		ArrayList<String> cmd2=new ArrayList<String>(Arrays.asList(params.zip));
		cmd2.add("a");
		cmd2.add("-tzip");
		cmd2.add("-y");
		cmd2.add(f1);
		cmd2.add(add);
		if (!run_command(cmd2))
		{
			Log.error("compressing failed for "+f1);
			return false;
		}
		return true;
	}

	public static void copy(File fromFile, File toFile) throws IOException
	{
		Log.debug("Copying File: "+fromFile.getAbsolutePath()+" To: "+toFile.getAbsolutePath());
		if (!fromFile.exists())
			throw new IOException("FileCopy: " + "no such source file: "
					+ fromFile.getName());
		if (!fromFile.isFile())
			throw new IOException("FileCopy: " + "can't copy directory: "
					+ fromFile.getName());
		if (!fromFile.canRead())
			throw new IOException("FileCopy: " + "source file is unreadable: "
					+ fromFile.getName());

		if (toFile.isDirectory())
			toFile = new File(toFile, fromFile.getName());

		if (toFile.exists())
		{
			if (!toFile.canWrite())
				throw new IOException("FileCopy: "+"destination file is unwriteable: " + toFile.getName());
		} else {
			String parent = toFile.getParent();
			if (parent == null)
				parent = System.getProperty("user.dir");
			File dir = new File(parent);
			if (!dir.exists())
				dir.mkdirs();
			if (dir.isFile())
				throw new IOException("FileCopy: "
						+ "destination is not a directory: " + parent);
			if (!dir.canWrite())
				throw new IOException("FileCopy: "
						+ "destination directory is unwriteable: " + parent);
		}
		FileInputStream from = null;
		FileOutputStream to = null;
		try {
			from = new FileInputStream(fromFile);
			to = new FileOutputStream(toFile);
			byte[] buffer = new byte[4096];
			int bytesRead;

			while ((bytesRead = from.read(buffer)) != -1)
				to.write(buffer, 0, bytesRead); // write
		} finally
		{
			if (from != null)
				try {
					from.close();
				} catch (IOException e) {
					;
				}
			if (to != null)
				try {
					to.close();
				} catch (IOException e) {
					;
				}
		}
	}
	/**
	 * Copy the content of one directory to another. recursively
	 * @param srcDir
	 * @param dstDir
	 * @return
	 * @throws IOException
	 */
	public static boolean copyDirectory(File srcDir, File dstDir) throws IOException
	{
		Log.debug("copying source: "+srcDir.getAbsolutePath()+" to: "+dstDir.getAbsolutePath());
		if (!srcDir.exists())
		{
			Log.error("could not find "+srcDir.getAbsolutePath());
			return false;
		}
		if (srcDir.isDirectory())
		{
			if (!dstDir.exists())
			{
				Log.debug("dest dir not exists, creating it.");
				dstDir.mkdirs();
			}

			String[] children = srcDir.list();
			for (int i=0; i<children.length; i++)
			{
				copyDirectory(new File(srcDir, children[i]),
						new File(dstDir, children[i]));
			}
		}
		else
		{
			// This method is implemented in Copying a File
			copy(srcDir, dstDir);
		}
		return true;
	}

	/** Deletes all files and subdirectories under dir.
	 * Returns true if all deletions were successful.
	 * If a deletion fails, the method stops attempting to delete and returns false.
	 * @param dir
	 * @return
	 */
	public static boolean deleteDir(File dir) {
		if (!dir.exists())
			return true;
		if (dir.isDirectory()) {
			String[] children = dir.list();
			for (int i=0; i<children.length; i++) {
				boolean success = deleteDir(new File(dir, children[i]));
				if (!success) {
					return false;
				}
			}
		}

		// The directory is now empty so delete it
		return dir.delete();
	}

	/**
	 * extract a folder or file from a zip file to current location
	 * @param f1 - zip file full path
	 * @param extract - what to extract
	 */
	public static boolean  extractZip(String f1,String extract)
	{
		Log.debug("Extracting "+extract+" from file: "+f1);
		ArrayList<String> cmd1=new ArrayList<String>(Arrays.asList(params.zip));
		cmd1.add("x");
		cmd1.add("-y");
		cmd1.add(f1);
		cmd1.add(extract);
		if (!run_command(cmd1))
		{
			Log.error("Extract failed for "+f1);
			return false;
		}
		return true;
	}
	/**
	 * extract specified files only from zip file to specified location
	 * @param file
	 * @param Dest
	 * @param filestoextract
	 * @return
	 */
	public static boolean  extractZipfiles(String file,String Dest,String filestoextract)
	{
		Log.debug("Extracting "+file+" to : "+Dest);
		ArrayList<String> cmd1=new ArrayList<String>(Arrays.asList(params.zip));
		cmd1.add("x");
		cmd1.add("-y");
		cmd1.add("-o\""+file+"\"");
		cmd1.add(filestoextract);
		if (!run_command(cmd1))
		{
			Log.error("Extract failed for "+file);
			return false;
		}
		return true;
	}

	/**
	 * extract full content of zip file to specified location
	 * @param file
	 * @param Dest
	 * @return
	 */
	public static boolean  extractTarToDest(String file,String Dest)
	{
		new File(Dest).mkdirs();
		ArrayList<String> cmd1;
		Log.debug("Extracting "+file+" to : "+Dest);
		if (System.getProperty("os.name").contains("Linux"))
		{
			cmd1=new ArrayList<String>(Arrays.asList(params.linuxextract));
			cmd1.add(file);
			cmd1.add("-C");
			cmd1.add(Dest);
		}
		else
		{
			cmd1=new ArrayList<String>(Arrays.asList(params.zip));
			cmd1.add("x");
			cmd1.add("-y");
			cmd1.add("-o\""+Dest+"\"");
			cmd1.add(file);
		}
		if (!run_command(cmd1,true))
		{
			Log.error("Extract failed for "+file);
			return false;
		}
		return true;
	}

	/**
	 * extract full content of zip file to specified location
	 * @param file
	 * @param Dest
	 * @return
	 */
	public static boolean  extractZipToDest(String file,String Dest)
	{
		new File(Dest).mkdirs();
		String[] s={"here","i","ams"};
		ArrayList<String> cmd1=new ArrayList<String>(Arrays.asList(s));
		Log.debug("Extracting "+file+" to : "+Dest);
		//			if (System.getProperty("os.name").contains("Linux"))
		//				cmd1=params.linuxextract+file+" -C "+Dest;
		//			else
		cmd1=new ArrayList<String>(Arrays.asList(params.zip));
		cmd1.add("x");
		cmd1.add("-y");
		cmd1.add("-o\""+Dest+"\"");
		cmd1.add(file);
		if (!run_command(cmd1,true))
		{
			Log.error("Extract failed for "+file);
			return false;
		}
		return true;
	}


	public static boolean extractZipToDest(String file, String Dest, String pass)
	{
		Log.debug("Extracting "+file+" to : "+Dest+" using pass: "+pass);
		ArrayList<String> cmd1=new ArrayList<String>(Arrays.asList(params.zip));
		cmd1.add("x");
		cmd1.add("-y");
		cmd1.add("-p\""+pass+"\"");
		cmd1.add("-o\"");
		if (!run_command(cmd1))
		{
			Log.error("Extract failed for "+file);
			return false;
		}
		return true;
	}

	/**
	 * Look for apk file.
	 * places assumed to be in - current dir. current dir\app\
	 * @param lookIn - folder to look in
	 * @param target - target name to match
	 * @param output - console handle to print messages
	 * @return
	 */
	public static File finapkdFile(String lookIn, String target)
	{
		File file=new File(lookIn);
		if (file.getName().compareTo(target)==0 && file.isFile())
			return file;

		file=new File(lookIn,target);
		if (file.exists())
			return file;

		file=new File(lookIn,"app"+File.separator+""+target);
		if (file.exists())
			return file;

		file=new File(lookIn,"system"+File.separator+"app"+File.separator+""+target);
		if (file.exists())
			return file;

		Log.debug("File "+target+" not found under: ."+lookIn);
		return null;
	}

	/**
	 * return file handle to file at the bottom of the tree if current dir
	 * is anywhere in the path
	 * 
	 * @param wholeTree - whole tree path including file name at the end
	 * @return - File handle if found, null if not
	 */
	public static File findFileDownTree(String project, String tree)
	{
		String[] parts=tree.split(java.util.regex.Pattern.quote(""+File.separator+""));
		File file=new File(project,tree);
		int i=0;
		while (!file.exists() && i<parts.length)
		{
			tree=tree.replace(parts[i]+File.separator,"");
			file=new File(project,tree);
			i++;
		}
		if (file.exists())
			return file;
		else
			return null;
	}
	/**
	 * Get all APK files found under given directory and Sub Directories
	 * @param input - directory to look in for aPK files
	 * @param pList - initialized ArrayList
	 * @return updates pList with APK files found
	 */
	public static void getApkFiles(AndroidFile input, ArrayList<AndroidFile> pList)
	//	
	{
		if (input==null)
			return;
		if (input.getName().endsWith(params.decompiledTag)) //this is a decompiled directory. do not search within it
			return;
		if( input.isDirectory() == true ){
			for(int i=0; i<input.list().length; i++){
				AndroidFile temp = new AndroidFile( input + File.separator + input.list()[i]);
				getApkFiles(temp,pList);
			}
		}
		else
		{
			java.io.FileFilter filter=new APKFileFilter();
			if (filter.accept(input))
				pList.add(input);
		}
	}
	/**
	 * Get all APK files found under given directory
	 * and Sub Directories. and assign to them the tag specified
	 * @param input
	 * @param pList
	 * @param tag
	 */
	public static void getApkFiles(AndroidFile input, ArrayList<AndroidFile> pList,String tag)
	//	
	{
		if (input==null)
			return;
		if (input.getName().endsWith(params.decompiledTag)) //this is a decompiled directory. do not search within it
			return;
		if( input.isDirectory() == true ){
			for(int i=0; i<input.list().length; i++){
				AndroidFile temp = new AndroidFile( input + File.separator + input.list()[i],tag);
				getApkFiles(temp,pList,tag);
			}
		}
		else{
			java.io.FileFilter filter=new APKFileFilter();
			if (filter.accept(input))
				pList.add(input);
		}
	}

	/**
	 *  Get all odex files located under given directory and subdirectories 
	 * @param input - directory to search within for odex files.
	 * @param pList - initialized ArrayList to which files will be added
	 * @return list is updated with the files found
	 */
	public static void getOdexFiles(File input, ArrayList<File> pList)
	//	return all odex under given input directory and subdirectories
	{
		if (input==null)
			return;
		if (input.getName().endsWith(params.decompiledTag))
			return;
		if( input.isDirectory() == true ){
			for(int i=0; i<input.list().length; i++){
				File temp = new File( input +File.separator+ input.list()[i]);
				getOdexFiles(temp,pList);
			}
		}
		else{
			java.io.FileFilter filter=new odexFileFilter();
			if (filter.accept(input))
				pList.add(input);
		}
	}

	public static boolean isOdex() {
		File t=new File(params.activeProject,"system"+File.separator+"app");
		if (!t.isDirectory())
		{
			Log.debug("app folder was not found at odex check");
			return false;
		}
		for (File a: t.listFiles())
		{
			if (a.getName().toLowerCase().endsWith(".odex"))
				return true;
		}

		t=new File(params.activeProject,"system"+File.separator+"framework");
		if (!t.isDirectory())
		{
			Log.debug("framework folder was not found at odex check");
			return false;
		}
		for (File a: t.listFiles())
		{
			if (a.getName().toLowerCase().endsWith(".odex"))
				return true;
		}

		t=new File(params.TranslateProject,"system"+File.separator+"app");
		if (!t.isDirectory())
		{
			Log.debug("app folder was not found at odex check");
			return false;
		}
		for (File a: t.listFiles())
		{
			if (a.getName().toLowerCase().endsWith(".odex"))
				return true;
		}

		t=new File(params.TranslateProject,"system"+File.separator+"framework");
		if (!t.isDirectory())
		{
			Log.debug("framework folder was not found at odex check");
			return false;
		}
		for (File a: t.listFiles())
		{
			if (a.getName().toLowerCase().endsWith(".odex"))
				return true;
		}

		return false;
	}
	
	public static boolean run_command(ArrayList<String> command)
	{
		return run_command(command, false,false,".");
	}
	
	public static boolean run_command(ArrayList<String> command,boolean outPut)
	{
		return run_command(command, false,outPut,".");
	}
	
	public static boolean run_command(ArrayList<String> command, boolean isApp,boolean output,String workDir)
	{
		int exitVal=1;
		try
		{       
			//run the batch file
			if (!isApp && !System.getProperty("os.name").contains("Linux")) {
				command.add(0, "cmd");
				command.add(1, "/c");
			}
			String[] cmds=new String[command.size()];
			cmds=command.toArray(cmds);
			File dir=new File(workDir);
			Log.debug("Run command: "+command+" From dir: "+dir.getAbsolutePath());
			Process proc = Runtime.getRuntime().exec(cmds,null,dir);
			
//			if (command.get(0).startsWith("java") || command.get(0).startsWith("\"java")) //if this is apktool.jar file replace error and debug output streams
//				//this is done to reverse the bug of apktool printing output to error stream
//			{
//				StreamGobbler errorGobbler = new StreamGobbler(proc.getInputStream(), "ERROR",output);            
//				errorGobbler.start();
//
//				// handle output buffer
//				StreamGobbler outputGobbler = new StreamGobbler(proc.getErrorStream(), "OUTPUT",output);
//				outputGobbler.start();				
//			}
//			else
//			{
				// handle error buffer
				StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR",output);            
				errorGobbler.start();

				// handle output buffer
				StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT",output);
				outputGobbler.start();    
//			}
			// get exit value
			//			proc.waitFor();
			exitVal = proc.waitFor();
			//			System.out.println("ExitValue: " + exitVal);        
		} catch(IOException ex)
		{
			ex.printStackTrace();
		} catch (InterruptedException ex)
		{
			ex.printStackTrace();
		}
		if (exitVal==0)
			return true;
		else
		{
			Log.error("Command "+command+" failed");
			return false;
		}
	}

	public static void listFolderContent(File folderPath,String currentPath,ArrayList<String> resources)
	{
		for (File f : folderPath.listFiles())
		{
			if (f.isFile())
				resources.add(currentPath+f.getName());
			else
				listFolderContent(f,currentPath+f.getName()+File.separator,resources);
		}
	}
	
	public static String trimCustom(String st, char c)
	{
		String s=st;
		if (s.startsWith(String.valueOf(c))) //remove leading c
			s=s.substring(1);
		if (s.endsWith(String.valueOf(c))) //remove leading c
			s=s.substring(0,s.length()-1);
		return s;
	}
}